
#include <arpa/inet.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef _DEFAULT_SOURCE
#define _DEFAULT_SOURCE
#endif
#include <sys/queue.h>
#include <unistd.h>

#include <event2/bufferevent.h>
#include <event2/event.h>
#include <event2/listener.h>

#include "cJSON.h"
#include "message_handle.h"

#define SERV_IP     "192.168.8.6"
#define SERV_PORT   9999
#define MAX_THREADS 4

/* 全局对象 */

/* 新增：全局链表，保存当前所有客户端 bufferevent */

struct client
{
    struct bufferevent *bev; /* 只保存指针，不访问内部 */
    TAILQ_ENTRY(client)
    next;
};
TAILQ_HEAD(client_list, client);
static struct client_list g_clients;
static pthread_mutex_t g_clients_mu = PTHREAD_MUTEX_INITIALIZER;

//
static pthread_t threads[MAX_THREADS];
static struct event_base *bases[MAX_THREADS];
static int quit_fds[2];        /* pipe 用于通知线程退出 */
static _Thread_local int tidx; /* 每个线程知道自己的序号 */

/* ---------------- 连接回调 ---------------- */
/* 新增：stdin 回调，把键盘输入发给所有客户端 */
static void stdin_cb(evutil_socket_t fd, short what, void *arg)
{
    (void)fd;
    (void)what;
    (void)arg;
    char buf[512] = { 0 };

    if (!fgets(buf, sizeof(buf), stdin)) /* Ctrl-D */
        return;

    size_t n = strlen(buf);
    pthread_mutex_lock(&g_clients_mu);

    /* 遍历自己定义的节点链表，取出 bev 再写 */
    struct client *c;
    TAILQ_FOREACH(c, &g_clients, next)
    bufferevent_write(c->bev, buf, n);

    pthread_mutex_unlock(&g_clients_mu);
}
/* 改写：echo_read_cb  ->  chat_read_cb
   收到客户端消息后，打印到终端，并原样回显（可去掉回显） */
static void chat_read_cb(struct bufferevent *bev, void *ctx)
{
    (void)ctx;
    char buf[512];
    int n;
    while ((n = bufferevent_read(bev, buf, sizeof(buf))) > 0) {
        fwrite(buf, 1, n, stdout); /* 打印到服务器终端 */
        fflush(stdout);
        /* 如果还想回显，保留下面一行；否则删掉就是纯“服务器转发” */
        // bufferevent_write(bev, buf, n);
    }
}
/* 改写：echo_event_cb 里把断开的客户端移出链表 */
static void echo_event_cb(struct bufferevent *bev, short events, void *ctx)
{
    (void)ctx;
    if (events & (BEV_EVENT_EOF | BEV_EVENT_ERROR)) {
        pthread_mutex_lock(&g_clients_mu);
        struct client *c = TAILQ_FIRST(&g_clients);
        while (c) {
            struct client *next = TAILQ_NEXT(c, next); /* 先存后继 */
            if (c->bev == bev) {
                printf("error occured user disconnect!\n");
                TAILQ_REMOVE(&g_clients, c, next);
                free(c);
                break; /* 一个 fd 对应一个节点，找到就停 */
            }
            c = next;
        }
        pthread_mutex_unlock(&g_clients_mu);
        bufferevent_free(bev);
    }
}

/* 新增：accept 成功时把 bev 加入链表 */
static void accept_cb(struct evconnlistener *lis,
                      evutil_socket_t fd,
                      struct sockaddr *addr, int socklen,
                      void *arg)
{
    (void)lis;
    (void)addr;
    (void)socklen;
    (void)arg;
    static _Atomic unsigned long rr = 0;
    int idx = (int)(rr++ % MAX_THREADS);
    struct event_base *dest = bases[idx];
    if (!dest) {
        close(fd);
        return;
    }

    struct bufferevent *bev = bufferevent_socket_new(
        dest, fd, BEV_OPT_CLOSE_ON_FREE);
    bufferevent_setcb(bev, chat_read_cb, NULL, echo_event_cb, NULL);
    bufferevent_enable(bev, EV_READ | EV_WRITE);

    /* 加入全局链表：先包一层节点再插入 */
    struct client *c = malloc(sizeof(*c));
    c->bev = bev;
    pthread_mutex_lock(&g_clients_mu);
    TAILQ_INSERT_TAIL(&g_clients, c, next);
    printf("user add!\n");
    pthread_mutex_unlock(&g_clients_mu);
}

/* ---------------- 工作线程 ---------------- */
/* 线程退出事件回调 */
static void stop_cb(evutil_socket_t fd, short what, void *arg)
{
    (void)fd;
    (void)what;
    struct event_base *base = arg;
    event_base_loopbreak(base);
}

static void *worker_thread(void *arg)
{
    int idx = *(int *)arg;
    free(arg); /* 传进来的是 malloc 的小块 */
    tidx = idx;

    struct event_base *base = event_base_new();
    if (!base) {
        perror("event_base_new");
        exit(EXIT_FAILURE);
    }
    bases[idx] = base;

    /* 监听退出管道 */
    struct event *ev_stop = event_new(base, quit_fds[0],
                                      EV_READ | EV_PERSIST,
                                      stop_cb, base);
    event_add(ev_stop, NULL);

    /* 进入循环 */
    event_base_dispatch(base);

    /* 清理 */
    event_free(ev_stop);
    event_base_free(base);
    bases[idx] = NULL;
    return NULL;
}

/* ---------------- main ---------------- */
int main(void)
{
    /* 忽略 SIGPIPE */
    signal(SIGPIPE, SIG_IGN);
    TAILQ_INIT(&g_clients); /* 千万别漏 */

    /* 创建退出管道 */
    if (pipe(quit_fds) != 0) {
        perror("pipe");
        return EXIT_FAILURE;
    }

    /* 创建工作线程 */
    for (int i = 0; i < MAX_THREADS; ++i) {
        int *p = malloc(sizeof *p);
        *p = i;
        int ret = pthread_create(&threads[i], NULL, worker_thread, p);
        if (ret != 0) {
            free(p); /* 立即释放 */
            perror("pthread_create");
            exit(EXIT_FAILURE);
        }
    }

    /* 等待所有线程 event_base_new 完成 */
    for (int i = 0; i < MAX_THREADS; ++i)
        while (!bases[i])
            usleep(1000);

    /* 主线程只负责监听 */
    struct sockaddr_in sin = {
        .sin_family = AF_INET,
        .sin_addr.s_addr = inet_addr(SERV_IP),
        .sin_port = htons(SERV_PORT)
    };

    struct event_base *main_base = event_base_new();
    if (!main_base) {
        perror("event_base_new main");
        return EXIT_FAILURE;
    }

    struct evconnlistener *listener =
        evconnlistener_new_bind(main_base, accept_cb, NULL,
                                LEV_OPT_REUSEABLE | LEV_OPT_CLOSE_ON_FREE,
                                -1, (struct sockaddr *)&sin, sizeof(sin));
    if (!listener) {
        perror("evconnlistener_new_bind");
        return EXIT_FAILURE;
    }
    /* 放在 main() 中，listener 创建之后 */
    struct event *ev_stdin = event_new(main_base, STDIN_FILENO,
                                       EV_READ | EV_PERSIST,
                                       stdin_cb, main_base);
    event_add(ev_stdin, NULL);

    printf("Echo server running on %s:%d  (threads=%d)\n",
           SERV_IP, SERV_PORT, MAX_THREADS);

    /* 主线程也进入循环（可被 Ctrl-C 中断） */
    event_base_dispatch(main_base);

    /* 通知工作线程退出 */
    for (int i = 0; i < MAX_THREADS; ++i)
        if (write(quit_fds[1], "x", 1) != 1)
            perror("write quit fd");

    /* 等待回收 */
    for (int i = 0; i < MAX_THREADS; ++i)
        pthread_join(threads[i], NULL);

    /* 清理 */
    evconnlistener_free(listener);
    event_base_free(main_base);
    close(quit_fds[0]);
    close(quit_fds[1]);
    return 0;
}